package com.abso.mp3tunes.locker.ui.views;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;

import com.abso.mp3tunes.locker.ui.ILockerConstants;
import com.abso.mp3tunes.locker.ui.LockerImages;
import com.abso.mp3tunes.locker.ui.LockerPlugin;
import com.abso.mp3tunes.locker.ui.actions.RefreshAction;
import com.abso.mp3tunes.locker.ui.actions.ShowAccountDataAction;
import com.abso.mp3tunes.locker.ui.actions.ToggleViewAction;
import com.abso.mp3tunes.locker.ui.preferences.LockerPreferencePage;

/**
 * Main class for the Locker browser view.
 */
public class LockerBrowserView extends ViewPart implements
		IPropertyChangeListener, ITabbedPropertySheetPageContributor,
		ISelectionProvider, ISelectionChangedListener {

	/**
	 * Indicates that the view is showing a special page asking for the Locker
	 * credentials.
	 */
	public static final int UNCONFIGURED_MODE = -1;

	/** Indicates that the view is showing a page with the list of artists. */
	public static final int ARTIST_MODE = 0;

	/** Indicates that the view is showing a page with the list of albums. */
	public static final int ALBUM_MODE = 1;

	/** Indicates that the view is showing a page with the list of playlists. */
	public static final int PLAYLIST_MODE = 2;

	/** Indicates that the view is showing a search page. */
	public static final int SEARCH_MODE = 3;

	/** The page book composite control. */
	private PageBook pageBook;

	/** The special panel asking the Locker credentials. */
	private LockerBrowserViewPanel unconfiguredPanel;

	/** The set of Locker view panels. */
	private LockerBrowserViewPanel[] lockerPanels;

	/** The actions able to switch between the different Locker view panels. */
	private ToggleViewAction[] viewActions;

	/** The current view mode. */
	private int currentViewMode = Integer.MIN_VALUE;

	/** The list of selection changed listeners. */
	private ListenerList selectionChangedListeners = new ListenerList();

	/** Constructs a new view. */
	public LockerBrowserView() {
		viewActions = new ToggleViewAction[] {
				new ToggleViewAction(this, ARTIST_MODE),
				new ToggleViewAction(this, ALBUM_MODE),
				new ToggleViewAction(this, PLAYLIST_MODE),
				new ToggleViewAction(this, SEARCH_MODE) };
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createPartControl(Composite parent) {
		pageBook = new PageBook(parent, SWT.NULL);

		/* adds pages to the page book */
		unconfiguredPanel = new UnconfiguredViewPanel(pageBook, this);
		lockerPanels = new LockerBrowserViewPanel[4];
		lockerPanels[ARTIST_MODE] = new ArtistViewPanel(pageBook, this);
		lockerPanels[ALBUM_MODE] = new AlbumViewPanel(pageBook, this);
		lockerPanels[PLAYLIST_MODE] = new PlaylistViewPanel(pageBook, this);
		lockerPanels[SEARCH_MODE] = new SearchViewPanel(pageBook, this);

		/* sets the filter menu items */
		IActionBars actionBars = getViewSite().getActionBars();
		IMenuManager viewMenu = actionBars.getMenuManager();
		for (int i = 0; i < viewActions.length; i++) {
			ToggleViewAction action = viewActions[i];
			viewMenu.add(action);
			action.setEnabled(true);
		}
		viewMenu.add(new Separator());
		viewMenu.add(new ShowAccountDataAction());
		viewMenu.add(new Action("Configure...") {
			public void run() {
				PreferenceDialog dialog = PreferencesUtil
						.createPreferenceDialogOn(getSite().getShell(),
								LockerPreferencePage.class.getName(), null,
								null);
				if (dialog.open() == Window.OK) {
					refreshCurrentViewer();
				}
			}
		});

		/* sets the filter toolbar */
		IToolBarManager tbmanager = actionBars.getToolBarManager();
		for (int i = 0; i < viewActions.length; i++) {
			tbmanager.add(viewActions[i]);
		}
		tbmanager.add(new Separator());
		tbmanager.add(new Action("Collapse All", LockerImages
				.getDescriptor(LockerImages.COLLAPSE_ALL)) {
			public void run() {
				if (currentViewMode != UNCONFIGURED_MODE) {
					lockerPanels[currentViewMode].collapseAll();
				}
			}
		});
		tbmanager.add(new RefreshAction(this));
		tbmanager.update(false);

		/* shows the artist view or the unconfigured view */
		if (!StringUtils.defaultString(
				LockerPlugin.getDefault().getPreferenceStore().getString(
						ILockerConstants.PREF_USERNAME)).equals("")) {
			viewActions[ARTIST_MODE].setChecked(true);
			setViewMode(ARTIST_MODE);
		} else {
			setViewMode(UNCONFIGURED_MODE);
		}

		/* listens to changes made on the MP3tunes preferences */
		LockerPlugin.getDefault().getPreferenceStore()
				.addPropertyChangeListener(this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
	 */
	public void init(IViewSite site) throws PartInitException {
		site.setSelectionProvider(this);
		super.init(site);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
	 */
	public void setFocus() {
		pageBook.setFocus();
	}

	/**
	 * Sets the current view mode.
	 * 
	 * @param viewMode
	 *            the current view mode.
	 */
	public void setViewMode(int viewMode) {
		if (viewMode != currentViewMode) {
			if (currentViewMode >= 0) {
				lockerPanels[currentViewMode].getLockerViewer()
						.removeSelectionChangedListener(this);
			}
			currentViewMode = viewMode;
			if (viewMode == UNCONFIGURED_MODE) {
				pageBook.showPage(unconfiguredPanel.getControl());
			} else {
				viewActions[viewMode].setChecked(true);
				pageBook.showPage(lockerPanels[currentViewMode].getControl());
				lockerPanels[currentViewMode].getLockerViewer()
						.addSelectionChangedListener(this);
				refreshCurrentViewer();
			}
		}
	}

	/** Sets the current viewer in a dirty state. */
	public void setCurrentViewerDirty() {
		setViewerDirty(currentViewMode);
	}

	/**
	 * Sets in a dirty state the viewer having the specified mode.
	 * 
	 * @param viewMode
	 *            the view mode.
	 * @see #ARTIST_MODE
	 * @see #ALBUM_MODE
	 * @see #PLAYLIST_MODE
	 * @see #SEARCH_MODE
	 */
	public void setViewerDirty(int viewMode) {
		if (viewMode != UNCONFIGURED_MODE) {
			lockerPanels[viewMode].getLockerViewer().setDirty();
		}
	}

	/** Updates the content of the current viewer. */
	public void refreshCurrentViewer() {
		if (currentViewMode != UNCONFIGURED_MODE) {
			final LockerBrowserViewPanel currentPanel = lockerPanels[currentViewMode];
			if (currentPanel.getLockerViewer().isDirty()) {
				Runnable runnable = new Runnable() {
					public void run() {
						currentPanel.getLockerViewer().updateContent();
					}
				};
				BusyIndicator.showWhile(getDisplay(), runnable);
			}
		}
	}

	/**
	 * Gets the current display.
	 * 
	 * @return the current display.
	 */
	private Display getDisplay() {
		if ((pageBook != null) && !(pageBook.isDisposed())) {
			return pageBook.getDisplay();
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent event) {
		if (ILockerConstants.PREF_USERNAME.equals(event.getProperty())
				|| (ILockerConstants.PREF_PASSWORD.equals(event.getProperty()))) {
			if (!event.getNewValue().equals(event.getOldValue())) {
				setViewerDirty(ARTIST_MODE);
				setViewerDirty(ALBUM_MODE);
				setViewerDirty(PLAYLIST_MODE);
				setViewerDirty(SEARCH_MODE);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor#getContributorId()
	 */
	public String getContributorId() {
		return ILockerConstants.ID_PROPERTY_CONTRIBUTOR;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
	 */
	public Object getAdapter(Class adapter) {
		if (adapter == IPropertySheetPage.class) {
			return new TabbedPropertySheetPage(this);
		} else {
			return super.getAdapter(adapter);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
	 */
	public void dispose() {
		LockerPlugin.getDefault().getPreferenceStore()
				.removePropertyChangeListener(this);
		super.dispose();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
	 */
	public void addSelectionChangedListener(ISelectionChangedListener listener) {
		selectionChangedListeners.add(listener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
	 */
	public ISelection getSelection() {
		if (currentViewMode != UNCONFIGURED_MODE) {
			return lockerPanels[currentViewMode].getLockerViewer()
					.getSelection();
		} else {
			return StructuredSelection.EMPTY;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
	 */
	public void removeSelectionChangedListener(
			ISelectionChangedListener listener) {
		selectionChangedListeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
	 */
	public void setSelection(ISelection selection) {
	}

	/**
	 * Notifies any selection changed listeners that the viewer's selection has
	 * changed. Only listeners registered at the time this method is called are
	 * notified.
	 * 
	 * @param event
	 *            a selection changed event
	 * 
	 * @see ISelectionChangedListener#selectionChanged
	 */
	protected void fireSelectionChanged(final SelectionChangedEvent event) {
		Object[] listeners = selectionChangedListeners.getListeners();
		for (int i = 0; i < listeners.length; ++i) {
			final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
			SafeRunnable.run(new SafeRunnable() {
				public void run() {
					l.selectionChanged(event);
				}
			});
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
	 */
	public void selectionChanged(SelectionChangedEvent event) {
		fireSelectionChanged(event);
	}

}
